#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <client.h>
#include <proto.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>

struct client_conf_st client_conf = {
    .rcvport = DEFAULT_RCVPORT,
    .mgroup = DEFAULT_MGROUP,
};

static const char* fifoFile = "/tmp/StreamBroadCastFifo";

static void print_help(void) {
    printf("-M或--mgroup 指定多播组\n");
    printf("-P或--port 指定接收端口\n");
    printf("-H或--help 打印帮助信息\n");
    fflush(stdout);
}

static void writeN(int fd, const void* buf, size_t len) {
    ssize_t cnt = 0;
    ssize_t n = 0;
    while (cnt < len) {
        n = write(fd, buf, len-cnt);
        if (n < 0) {
            perror("write");
            exit(1);
        }
        else {
            cnt = cnt + n;
        }
    }
}

/*
    -M或--mgroup 指定多播组
    -P或--port 指定接收端口
    -p或--plaer 指定播放器
    -H或--help 打印帮助信息
*/
int main(int argc, char** argv) {

    /*
    初始化
    级别：默认值，配置文件，环境变量，命令行参数
    */
    struct option argvOpt[] = {
        {"port", 1, NULL, 'P'},
        {"mgroup", 1, NULL, 'M'},
        {"help", 0, NULL, 'H'},
        {NULL, 0, NULL, 0},
    };
    int c = 0;
    int index = 0;
    while (1) {
        c = getopt_long(argc, argv, "MPH", argvOpt, &index);
        if (c < 0) {
            break;
        }
        switch (c) {
            case 'P':
                client_conf.rcvport = optarg;
                break;
            case 'M':
                client_conf.mgroup = optarg;
                break;
            case 'H':
                print_help();
                exit(0);
                break;
            default:
                abort();
                break;
        }
    }
    
    int sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd < 0) {
        perror("socket");
        exit(1);
    }

    struct ip_mreqn mreq;
    inet_pton(AF_INET, client_conf.mgroup, &mreq.imr_multiaddr);
    inet_pton(AF_INET, "0.0.0.0", &mreq.imr_address);
    mreq.imr_ifindex = if_nametoindex(IF_INTERFACE);
    if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) {// 加入多播组
        perror("setsockopt");
        close(sd);
        exit(1);
    }

    int val = 1;
    if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &val, sizeof(val))) {
        perror("");
        close(sd);
        exit(1);
    }

    struct sockaddr_in localAddr;
    localAddr.sin_family = AF_INET;
    inet_pton(AF_INET, "0.0.0.0",(void*)&localAddr.sin_addr.s_addr);
    localAddr.sin_port = htons(atoi(client_conf.rcvport));
    if (bind(sd, (void*)&localAddr, sizeof(localAddr))) {
        perror("bind");
        close(sd);
        exit(1);
    }

    // 父子进程管道通信
    int ret = -1;
	//umask(0);
	(void)remove(fifoFile);
    ret = mkfifo(fifoFile, 777);	// 在文件系统创建有名管道
	if (ret == -1) {
		fprintf(stderr, "parent process mkfifo error,%s\n", strerror(errno));
		close(sd);
		exit(EXIT_FAILURE);
	}
	fprintf(stdout, "parent process mkfifo successed\n");

	int pd = open(fifoFile, O_RDWR);
	if (pd == -1) {
		fprintf(stderr, "open error,%s\n", strerror(errno));
		close(sd);
		exit(EXIT_FAILURE);
	}

    // 父进程：从网络收报，发送给子进程
    // 子进程：调用解码器
    pid_t pid;
    pid = fork();
    if (pid < 0) {
        perror("fork");
        close(sd);
        exit(1);
    }

    if (pid == 0) {    // 子进程
         close(sd);
		 int ret = dup2(pd, STDIN_FILENO); // 管道接收端重定向为标准输入
         if (ret == -1) {
			fprintf(stderr, "child process dup2 error,%s\n", strerror(errno));
			exit(1);
		}
		char* cmd[] = {"/usr/bin/mpg123", "-", (char*)NULL};   // -代表标准输入
        execv("/usr/bin/mpg123", cmd);
		perror("execl error");
        exit(1);
    }
    else if (pid > 0) { // 父进程
        // printf("this is parent\n");
        // 接收节目单
        unsigned char channelMsg[MSG_LIST_MAX] = {0};
        struct msg_list_st* msg_list = (struct msg_list_st*)channelMsg;
        struct sockaddr_in serverAddr;
        socklen_t serverLen;
        ssize_t n = 0;
        
        while (1) {
            memset(msg_list, 0, MSG_LIST_MAX);
            n = recvfrom(sd, msg_list, MSG_LIST_MAX, 0, (void*)&serverAddr, (void*)&serverLen);
            if (n < sizeof(struct msg_list_st)) {
                fprintf(stderr, "not struct msg_list_st\n");
                continue;
            }

            if (msg_list->chnid != LSITCHNID) {
                fprintf(stderr, "id not match\n");
                continue;
            }

            break;
        }
        
        // 打印节目单
        struct msg_listentry_st* pos = msg_list->entry;
        while (1) {
            if (pos->chnid < MINCHNID) {
                break;
            }
            uint16_t len = ntohs(pos->len); // 当前频道概览信息长度
            printf("chnid:%d, desc:%s\n", pos->chnid, pos->desc);
            pos = (struct msg_listentry_st*)((char*)pos + len);
        }

        // 选择频道
        int chosenid = -1;
        for (;;) {
            int ret = scanf("%d", &chosenid);
            if (ret == 1) {
                break;
            }
        }
        
        struct msg_channel_st* msg_channel = NULL;
        unsigned char mediaMsg[MSG_CHANNEL_MAX] = {0};
        msg_channel = (struct msg_channel_st*)mediaMsg;

        ssize_t rcvN = 0;
        struct sockaddr_in remoteAddr;
        socklen_t remoteLen;
        while (1) {
            memset(msg_channel, 0, MSG_CHANNEL_MAX);
            rcvN = recvfrom(sd, msg_channel, MSG_CHANNEL_MAX, 0, (void*)&remoteAddr, &remoteLen);
            if (rcvN < 0) {
                perror("malloc");
                close(pd);
                close(sd);
                exit(1);
            }
            else if (rcvN == 0) {
                continue;
            }

            // if (remoteAddr.sin_addr.s_addr != serverAddr.sin_addr.s_addr) {
            //     fprintf(stderr, "not from server\n");
            //     continue;
            // }
            // if (remoteAddr.sin_port != serverAddr.sin_port) {
            //     fprintf(stderr, "not from server\n");
            //     continue;
            // }

            if (rcvN < sizeof(struct msg_channel_st)) {
                fprintf(stderr, "invalid msg for len too short\n");
                continue;
            }

            // 收频道包
            if (msg_channel->chnid == chosenid) {
                //write(STDOUT_FILENO, (char*)(msg_channel->data), rcvN);
                writeN(pd, msg_channel->data, rcvN-sizeof(chnid_t));
            }
        }
        
        close(pd);
        close(sd);

        exit(0);
        
    }

    exit(0);
}
